use crate::controller::Error;
use crate::controller::error::ObjectHasBeenReferenceError;
use crate::dto;
use crate::service::ServiceSet;
use std::sync::Arc;

pub struct SegmentListController {
    service_set: Arc<ServiceSet>,
}

impl SegmentListController {
    pub fn new(service_set: Arc<ServiceSet>) -> Self {
        SegmentListController { service_set }
    }

    pub async fn add(
        &self,
        request: &dto::segment_list::AddRequest,
    ) -> Result<dto::core::segment_list::AddResponseData, Error> {
        let data = self.service_set.segment_list_manager.add(request).await?;
        Ok(data)
    }

    pub async fn list_info(
        &self,
        request: &dto::segment_list::ListRequest,
    ) -> Result<dto::segment_list::ListInfoResponseData, Error> {
        let total_count = self
            .service_set
            .segment_list_manager
            .count(&request.filter)
            .await?;

        if total_count == 0 {
            return Ok(dto::segment_list::ListInfoResponseData {
                total_count,
                list: Vec::new(),
            });
        }

        let list = self
            .service_set
            .segment_list_manager
            .list_info(&request.filter, request.offset, request.limit)
            .await?;

        Ok(dto::segment_list::ListInfoResponseData { total_count, list })
    }

    pub async fn get_info(
        &self,
        request: &dto::segment_list::GetRequest,
    ) -> Result<dto::segment_list::SegmentListInfo, Error> {
        let data = self
            .service_set
            .segment_list_manager
            .get_info(request.segment_list_id.as_str())
            .await?;

        Ok(data)
    }

    pub async fn update(&self, request: &dto::segment_list::UpdateRequest) -> Result<(), Error> {
        if request.params == Default::default() {
            return Ok(());
        }

        self.service_set
            .segment_list_manager
            .update(request.segment_list_id.as_str(), &request.params)
            .await?;

        Ok(())
    }

    pub async fn delete(&self, request: &dto::segment_list::DeleteRequest) -> Result<(), Error> {
        self.check_has_been_reference(request.segment_list_id.as_str())
            .await?;

        self.service_set
            .segment_list_manager
            .delete(request.segment_list_id.as_str())
            .await?;

        Ok(())
    }

    async fn check_has_been_reference(&self, segment_list_id: &str) -> Result<(), Error> {
        if let Some(v) = self
            .service_set
            .candidate_path_segment_list_manager
            .list(
                &dto::core::candidate_path_segment_list::Filter {
                    segment_list_id: Some(segment_list_id.to_string()),
                    ..Default::default()
                },
                0,
                1,
            )
            .await?
            .get(0)
        {
            return Err(
                ObjectHasBeenReferenceError::candidate_path(v.candidate_path_id.as_str()).into(),
            );
        }

        Ok(())
    }
}
